/*
 * Copyright (C) 2010 TopCoder Inc., All Rights Reserved.
 */
package com.hercules.xre.protocol.testing.netty.http;

import com.hercules.xre.protocol.testing.EventHandler;
import com.hercules.xre.protocol.testing.Helper;
import com.hercules.xre.protocol.testing.netty.ChannelExceptionHandler;
import com.hercules.xre.protocol.testing.netty.XREEventsHandler;
import com.topcoder.util.log.Log;
import com.topcoder.util.log.LogManager;

import javax.net.ssl.SSLEngine;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.ssl.SslHandler;
/**
 * <p>
 * Pipeline Factory that is responsible for initialization of channel handlers pipeline
 * for HTTP based connections.
 * </p>
 * <p>
 * <strong>Thread-safety:</strong> This class is thread safe as it's immutable.
 * </p>
 * @author TCSDESIGNER, TCSDEVELOPER
 * @version 1.0
*/
public class HTTPConnectionChannelPipelineFactory implements ChannelPipelineFactory {
    /**
     * <p>
     * Logger that is used to log activities and errors.
     * It is initialized to LogManager.getLog(HTTPConnectionChannelPipelineFactory.class.getName()).
     * Can not be null, never changes after initialization.
     * </p>
     */
    private static final Log LOGGER = LogManager.getLog(HTTPConnectionChannelPipelineFactory.class.getName());

    /**
     * <p>
     * Connection eventHandler that will be used by channel handlers to inform application
     * about communication events.
     * </p>
     * <p>
     * It is set in the constructor and never changes after that.
     * </p>
     */
    private final EventHandler eventHandler;

    /**
     * <p>
     * Connection manager that will be used by channel handlers to get serializes,
     * search for channels by sessionGUID etc.
     * </p>
     * <p>
     * It is set in the constructor and never changes after that.
     * </p>
     */
    private final HTTPProtocolTestingConnectionManager  connectionManager;

    /**
     * <p>
     * Name of HTTP Request parameter that will contain XRE events.
     * Can't be null, can't be empty, doesn't change after initialization.
     * </p>
     */
    private final String requestParameterName;

    /**
     * <p>
     * Creates new instance, it simply sets passed parameters to appropriate fields.
     * </p>
     * @param eventHandler event handler that will be used to report detected events, can't be null
     * @param requestParameterName the name of request parameter, can't be null, can't be empty
     * @param connectionManager connection manager, can't be null
     * @throws IllegalArgumentException when any attribute is invalid
    */
    public HTTPConnectionChannelPipelineFactory(HTTPProtocolTestingConnectionManager connectionManager,
        EventHandler eventHandler, String requestParameterName) {
        final String signature = "HTTPConnectionChannelPipelineFactory#ctor";
        Helper.logEnter(signature, new String[] {"connectionManager", "eventHandler", "requestParameterName"},
            new Object[] {connectionManager, eventHandler, requestParameterName}, LOGGER);
        try {
            Helper.checkNull(connectionManager, "connectionManager", signature, LOGGER);
            Helper.checkNull(eventHandler, "eventHandler", signature, LOGGER);
            Helper.checkString(requestParameterName, "requestParameterName", signature, LOGGER);
            this.connectionManager = connectionManager;
            this.eventHandler = eventHandler;
            this.requestParameterName = requestParameterName;
        } finally {
            Helper.logExit(signature, null, LOGGER);
        }
    }

    /**
     * <p>
     * Creates new channel pipeline that will be used to handle HTTP communication.
     * </p>
     * @return ChannelPipeline instance.
    */
    public ChannelPipeline getPipeline() {
        final String signature = "HTTPConnectionChannelPipelineFactory#getPipeline";
        Helper.logEnter(signature, null, null, LOGGER);
        ChannelPipeline pipeline = Channels.pipeline();
        // add a handler for exceptions
        pipeline.addLast("exceptionsHandler", new ChannelExceptionHandler());
        // if encryption  is to be used add SSL handler as a first handler
        SSLEngine engine = connectionManager.getSSLEngine();
        if (engine != null) {
            pipeline.addLast("ssl", new SslHandler(engine));
        }
        // request decoder
        pipeline.addLast("decoder", new HttpRequestDecoder());

        // gather large requests into one single message
        pipeline.addLast("aggregator", new HttpChunkAggregator(Integer.MAX_VALUE));

        // HTTP response encoder
        pipeline.addLast("encoder", new HttpResponseEncoder());

        // request handler that will fetch serialization events from request content,
        // and will pass it down the pipeline for appropriate handling
        pipeline.addLast("requestHandler", new HTTPRequestHandler(requestParameterName));

        // handler that is responsible for de-serializing and dispatching events
        pipeline.addLast("XREEventsHandler", new XREEventsHandler(connectionManager, eventHandler));

        // handler that is responsible for gathering all XRECommands and sending
        // them to the client in response to client's request
        pipeline.addLast("commandHandler", new HTTPCommandHandler(connectionManager, eventHandler));

        return Helper.logExit(signature, pipeline, LOGGER);
    }
}

